#define _CRT_SECURE_NO_WARNINGS

#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "tire.h"
#include "scanner.h"

#define ERROR_CHECK(ret, error_flag, msg) \
    do { \
        if ((ret) == (error_flag)) { \
            perror(msg); \
            exit(1); \
        } \
    } while(0)

typedef struct {
	const char* start;		// ָ��ǰ����ɨ���Token����ʼ�ַ�
	const char* current;	// �ʷ���������ǰ���ڴ�����Token���ַ�,һ��ʼ������start��ʼ,ֱ������������Token,ָ���Token����һ���ַ�
	int line;		// ��¼��ǰToken��������
} Scanner;

// ȫ�ֱ���
static Scanner scanner;

// OK
void initScanner(const char* source) {
	scanner.start = source;
	scanner.current = source;
	scanner.line = 1;
}

// OK
// �жϵ�ǰ�ַ��Ƿ���ĸ���»���
static bool isAlpha(char c) {
	return (c >= 'a' && c <= 'z') ||
		(c >= 'A' && c <= 'Z') ||
		c == '_';
}

// OK
// �жϵ�ǰ�ַ��ǲ�������
static bool isDigit(char c) {
	return c >= '0' && c <= '9';
}

// OK
// �ж�Scanner��ǰ���ڴ������ַ��ǲ��ǿ��ַ�,�ж��ǲ��Ǵ�������
static bool isAtEnd() {
	return *scanner.current == '\0';
}


// OK
// currָ��ǰ��һ���ַ�,������֮ǰcurrָ��ָ���Ԫ��
static char advance() {
	return *scanner.current++;
}

// OK
// �鿴��ǰ���ڴ������ַ���ʲô,curr����
static char peek() {
	return *scanner.current;
}

// OK
// ��ǰ�ǿ��ַ�,Ƴһ����һ���ַ���ʲô,curr����
static char peekNext() {
	if (isAtEnd()) {
		return '\0';
	}
	return *(scanner.current + 1);
}


// OK
// �жϵ�ǰ���ڴ������ַ��ǲ��Ƿ���Ԥ��,�������currǰ��һλ
static bool match(char expected) {
	if (isAtEnd()) {
		return false;	// ������ڴ������ǿ��ַ�,�Ǿͷ���false
	}
	if (peek() != expected) {
		return false;	// ���������Ԥ��,Ҳ����false
	}
	scanner.current++;
	return true;	// ֻ�з���Ԥ�ڲŻ᷵��true ����curr��ǰ��һλ
}


// OK
// ���ݴ����TokenType���������췵��һ��Token
static Token makeToken(TokenType type) {
	Token token;
	token.type = type;
	token.start = scanner.start;
	token.length = (int)(scanner.current - scanner.start);	// ����Token�ַ����ĳ���
	token.line = scanner.line;
	return token;
}


// OK
// �������ܽ��������ʱ�����Ǵ���һ��ERROR Token. ���磺����@��$�ȷ���ʱ�������ַ������ַ�û�ж�Ӧ��������ʱ��
static Token errorToken(const char* message) {
	Token token;
	token.type = TOKEN_ERROR;
	token.start = message;
	token.length = (int)strlen(message);
	token.line = scanner.line;
	return token;
}



/// <summary>
/// �˶δ�������Ż�
/// </summary>
/// <param name="ch"></param>
/// <returns></returns>
static bool isSkip(char ch) {
	return ch == ' ' || ch == '\r' || ch == '#' || ch == '\n' || ch == '/' || ch == '\t';
}



// OK
// need implement
static void skipWhitespace() {
	// �����հ��ַ�: ' ', '\r', '\t', '\n'��ע��
	// ע����'//'��ͷ, һֱ����β
	// ע�����scanner.line��

	while (isSkip(peek())) {
		if (peek() == '#' || (peek() == '/' && peekNext() == '/')) {
			
			while (peek() != '\n'){
				advance();
			}
			
		}else if(peek() == '\r' || peek() == ' ' || peek() == '\t') {
			advance();
		}

		if (peek() == '\n') {
			advance();
			scanner.line++;
		}
	}
}





// OK
// ���ڼ�鵱ǰɨ���Token�������ǲ���type ����Ǿͷ���type
static TokenType checkKeyword(int start, int length, const char* rest, TokenType type) {
	if (scanner.current - scanner.start == start + length && memcmp(scanner.start + start, rest, length) == 0) {
		return type;
	}
	return TOKEN_IDENTIFIER;
}






// OK
// need implement
static TokenType identifierType() {
	
	TokenType type = query(scanner.start, scanner.current - scanner.start);
	if (type == -1) {
		return TOKEN_IDENTIFIER;
	}

	return type;
}












// OK
// ��ǰToken�Ŀ�ͷ���»��߻���ĸ�ж����ǲ��Ǳ�ʶ��Token
static Token identifier() {

	// �ж�currָ�뵱ǰ���ڴ������ַ��Ƿ���ĸ���»��ߡ�����
	while (isAlpha(peek()) || isDigit(peek())) {
		advance();
	}

	return makeToken(identifierType());
}


// ����δ����
// need implement
static Token number() {
	// ����������ǽ�NUMBER�Ĺ���������:
	// 1. NUMBER���԰������ֺ����һ��'.'��
	// 2. '.'��ǰ��Ҫ������
	// 3. '.'�ź���ҲҪ������
	// ��Щ���ǺϷ���NUMBER: 123, 3.14
	// ��Щ���ǲ��Ϸ���NUMBER: 123., .14(��Ȼ��C�����кϷ�)
	// ��ʾ: ���������Ҫ���ϵ������м�����ְ���С����,����Ҳ��Ҫѭ��
	bool flag = 1;

	while (isDigit(peek()) || flag) {
		if (peek() == '.') {
			flag = 0;
		}

		advance();
	}
	return makeToken(TOKEN_NUMBER);
}

// OK
// need implement
static Token string() {
	// �ַ�����"��ͷ����"��β�����Ҳ��ܿ���
	// Ϊ�˼򻯹�����,�����ַ���
	// �����һ���ַ�����ĩβҲ����˫���ţ�ȫ������(curr���Լ�¼���ȣ����õ���)
	advance();
	while (peek() != '"' && peek() != EOF) {
		if (peek() == '\n') {
			perror("it is not a string");
			exit(-1);
		}
		advance();
	}
	advance();
	return makeToken(TOKEN_STRING);
}


// need implement
static Token character() {
	// �ַ�'��ͷ����'��β�����Ҳ��ܿ���
	// �����һ���ַ�����ĩβҲ���ǵ����ţ�ȫ������(curr���Լ�¼���ȣ����õ���)
	// ��������������˵һģһ����Ҳ�Ǽ���һ��
	advance();
	if (peek() == '\n') {
		perror("to many characters in constant");
		exit(-1);
	}
	
	if (advance() != '\'') {
		perror("not a character");
		exit(-1);
	}
	//advance();

	return makeToken(TOKEN_CHARACTER);
}

// OK
// �����޷�ʶ����ַ�
static Token errorTokenWithChar(char character) {


	// �˴���bug��Ҫ�ĳɶ���
	char message[50];
	// ���޷�ʶ����ַ���ʲô���
	sprintf(message, "Unexpected character: %c", character);
	return errorToken(message);
}


// need implement
// Scanner�����߼�,���ڷ���һ�������õ�Token
Token scanToken() {
	
	skipWhitespace();
	// ��¼��һ��Token����ʼλ��
	scanner.start = scanner.current;


	if (isAtEnd()) {
		return makeToken(TOKEN_EOF);
	}

	char c = advance();

	// ���Token�ĵ�һ���ַ�����ĸ���»���
	if (isAlpha(c)) {
		return identifier();
	}

	// ���Token�ĵ�һ���ַ�������
	if (isDigit(c)) {
		return number();
	}

	// ���Token�ĵ�һ���ַ��Ȳ�������Ҳ������ĸ���»���
	switch (c) {
		// ��һ����: �������ַ�Token
		case '(': 
			return makeToken(TOKEN_LEFT_PAREN);
		case ')': 
			return makeToken(TOKEN_RIGHT_PAREN);


				// ̰���㷨���ܲ����ٸ�һ����
		case '+':
			// ���Token�ĵڶ����ַ�Ҳ��+,�Ǿ�����++˫�ַ�Token����
			if (match('+')) {
				return makeToken(TOKEN_PLUS_PLUS);
			}
			else if (match('=')) {
				return makeToken(TOKEN_PLUS_EQUAL);
			}
		
		case '"': 
			return string(); // ���Token�ĵ�һ���ַ���˫����,�Ǿͽ������ַ���Token����

		case '\'': 
			return character();	// ���Token�ĵ�һ���ַ��ǵ�����,�Ǿͽ������ַ�Token����

		case ';':
			return makeToken(TOKEN_SEMICOLON);

		case '{':
			return makeToken(TOKEN_LEFT_BRACE);
		case '}':
			return makeToken(TOKEN_RIGHT_BRACE);
		case '=':
			return makeToken(TOKEN_EQUAL);
	}

	// �������������û�д����ɹ�,˵�����ַ��޷�ʶ��
	return errorTokenWithChar(c);
}
